package com.zenika.zenbinding.client.handler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.zenika.zenbinding.client.model.ListModel;
import com.zenika.zenbinding.client.model.NullValueModel;
import com.zenika.zenbinding.client.model.ValueModel;

/**
 * Abstract class extended by an handler bounds with a ListModel. It implements the Observer pattern to notify the ListModel in case of change in the elements selection.
 * 
 * @author Zenika
 */
public abstract class ListBindingHandler implements Handler {
  
  private List<ListBindingListener> listenerList;
  protected HashMap<String, Object> valueMap;
  
  public ListBindingHandler() {
    this.listenerList = new ArrayList<ListBindingListener>();
    this.valueMap = new HashMap<String, Object>();
  }
  
  protected void setChoices(List<ValueModel> valueList) {
    Iterator<ValueModel> iter = valueList.iterator();
    while ( iter.hasNext() ) {
      ValueModel model = iter.next();
      String value = model.getValue();
      this.valueMap.put(value, model.getObject());
      this.addItem(value);
    }
  }
  
  public void fireChange(List<ListBindingListener> selectedItems) {
    Iterator<ListBindingListener> iter = this.listenerList.iterator();
    while ( iter.hasNext() ) {
      iter.next().onChange(selectedItems, this);
    }
  }
  
  public void addListBindingListener(ListBindingListener listener) {
    this.listenerList.add(listener);
  }
  
  public void removeListBindingListener(ListBindingListener listener) {
    this.listenerList.remove(listener);
  }
  
  public abstract void setErrorStyle();
  
  public abstract void setRegularStyle();
  
  public void setListModel(ListModel model) {
    model.setHandler(this);
    this.addListBindingListener(model);
    this.setSelectedItems(model.getList(this));
  }
  
  /**
   * Add an item in the selection of the handled widget.
   * 
   * @param item
   *          String displayed
   */
  public abstract void addItem(String item);
  
  /**
   * Get the object represented by the String item.
   * 
   * @param item
   * @return
   */
  public Object getObject(String item) {
    return this.valueMap.get(item);
  }
  
  public abstract void setItemSelected(String item, boolean selected);
  
  /**
   * Set the items which have to be shown as selected.
   * 
   * @param selectedItems
   *          List of selected items.
   */
  private void setSelectedItems(List<?> selectedItems) {
    Iterator<?> iter = selectedItems.iterator();
    while ( iter.hasNext() ) {
      Object selectedObject = iter.next();
      
      Set<String> keys = this.valueMap.keySet();
      Iterator<String> iter2 = keys.iterator();
      while ( iter2.hasNext() ) {
        String item = iter2.next();
        
        // if this is a fake entry that acts as blank
        boolean isBlank = NullValueModel.VALUE.equals(item);
        
        if ( (isBlank && (selectedObject == null)) || ((this.valueMap.get(item) != null) && this.valueMap.get(item).equals(selectedObject)) ) {
          this.setItemSelected(item, true);
        }
      }
    }
  }
  
}
